using System;
using System.Collections.Generic;
using System.Text;
using System.Drawing;
using System.Windows.Forms;
using Animation;

namespace Controls
{
	public class Canvas : Panel
	{
		public void InitTransparentStyle()
		{
			this.SetStyle(ControlStyles.AllPaintingInWmPaint | ControlStyles.UserPaint | ControlStyles.OptimizedDoubleBuffer, true);
		}
	
		protected override void OnPaintBackground(PaintEventArgs pevent)
		{
			// no.
		}
	}

    public delegate void EventTimelineCurrentFrameChanged(object sender, int frame);
    public class TimeLineControl : Control
    {
        private int m_lmargin = 2;
        private int m_rmargin = 12;
        public event EventTimelineCurrentFrameChanged FrameChanged;

        // Invoke the FrameChanged event; called whenever list changes
        protected virtual void OnFrameChanged(object sender, int frame)
        {
            if (FrameChanged != null)
                FrameChanged(this, frame);
        }

        private Timeline m_timeline;

        public Timeline Timeline
        {
            get {
                return m_timeline;
            }
            set { 
                m_timeline = value; 
                if( m_timeline != null )
                    m_currentFrame = m_timeline.CurrentFrame;
                m_iSelectionBegin = 0;
                m_iSelectionEnd = 0;
            }
        }

		// TODO: This should be part of Timeline
        private int m_currentFrame = 0;
        private int m_iSelectionBegin = 0;
        private int m_iSelectionEnd = 0;

        private ToolTip m_tooltip;

        public TimeLineControl()
        {
            BackColor = Color.DarkGray;
            m_tooltip = new ToolTip();
        }

        public TimeLineControl(Control parent, Timeline tl)
        {
            m_timeline = tl;
        }

        public int CurrentFrame
        {
            get { return m_currentFrame; }
            //set { m_currentFrame = value; }
        }

        protected override void OnMouseDown(MouseEventArgs e)
        {
            base.OnMouseClick(e);

			double x = TimeFromMouseCoord(e.X - m_lmargin);

			if (x < 0) x = 0;
			if (x > m_timeline.TotalSeconds) x = m_timeline.TotalSeconds;

			m_currentFrame = (int)Math.Truncate(x * m_timeline.FrameRate);
			m_timeline.CurrentFrame = m_currentFrame;

			// fix the current frame in case the timeline "clamped" it
			m_currentFrame = m_timeline.CurrentFrame;


            if (e.Button == MouseButtons.Right)
            {
				if ((m_currentFrame < m_iSelectionBegin) || (m_currentFrame > m_iSelectionEnd))
				{
					m_iSelectionBegin = m_currentFrame;
					m_iSelectionEnd = m_currentFrame;
				}

				ContextMenuStrip.Show(this, new Point(e.X, e.Y));
            } else {
				m_iSelectionBegin = m_currentFrame;
				m_iSelectionEnd = m_currentFrame;
			}

			OnFrameChanged(this, m_currentFrame);
			Refresh();
        }

        protected override void OnMouseMove(MouseEventArgs e)
        {
            base.OnMouseMove(e);
 
            m_tooltip.SetToolTip(this, TimeStringFromMouseCoord(e.X-m_lmargin));
 
            if(e.Button != MouseButtons.Left)
				return;

			double x = TimeFromMouseCoord(e.X - m_lmargin);

			if (x < 0) x = 0;
			if (x > m_timeline.TotalSeconds) x = m_timeline.TotalSeconds;

			m_currentFrame = (int)Math.Truncate(x * m_timeline.FrameRate);

			m_timeline.CurrentFrame = m_currentFrame;

			// fix the current frame in case the timeline "clamped" it
			m_currentFrame = m_timeline.CurrentFrame;

			if (m_currentFrame < m_iSelectionBegin)
			{
				m_currentFrame = m_iSelectionBegin;
				m_iSelectionEnd = m_iSelectionBegin;
			} else {
				m_iSelectionEnd = m_currentFrame;
			}

			OnFrameChanged(this, m_currentFrame);
			Refresh();

        }

        /// <summary>
        ///  Inserts a key frame in the current frame
        /// </summary>
        /// <returns>The newly created Keyframe instance or the existing one</returns>
        public Keyframe AddKeyFrame()
        {
            return m_timeline.GetKeyFrame(m_currentFrame);
        }


		public void InsertNewFrames()
		{
			Skeletons.FormInsertNewFrames f = new Skeletons.FormInsertNewFrames();
			if(f.ShowDialog() != System.Windows.Forms.DialogResult.OK)
			{
				f.Dispose();
				return;
			}
			int iCount = f.FrameCount();

            f.Close();
			//f.Dispose();
			
			m_timeline.InsertFrames(Timeline.CurrentFrame,iCount);
			Refresh();
		}
		
		public void DeleteSelectedFrames()
		{
            try
            {
                m_timeline.RemoveFrames(m_iSelectionBegin, m_iSelectionEnd - m_iSelectionBegin + 1);
            }
            catch
            {
                MessageBox.Show("L'intervallo selezionato contiene Keyframe. E' necessario cancellarli prima e riprovare.", "Elimina Frame", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
                return;
            }

			m_iSelectionEnd = m_iSelectionBegin;
			m_currentFrame = m_iSelectionBegin;
            if (m_currentFrame != m_timeline.CurrentFrame)
            {
                m_timeline.CurrentFrame = m_currentFrame;
                OnFrameChanged(this, m_currentFrame);
            }
            Refresh();
        }
		
        protected override void OnBackColorChanged(EventArgs e)
        {
            base.OnBackColorChanged(e);
        }
        

        protected override void OnPaint(PaintEventArgs e)
        {
            base.OnPaint(e);
            System.Drawing.Font fnt = new Font(Parent.Font.FontFamily, (float)8);
            //e.Graphics.FillRectangle(Brushes.DarkGray, this.ClientRectangle);
            if (this.Focused)
            {
                //Pen p = Pens.Crimson;
                //p.DashStyle = System.Drawing.Drawing2D.DashStyle.Dot;
                //e.Graphics.DrawRectangle(p, 2, 2, Width - 4, Height - 4);
            }
            e.Graphics.DrawString("Frame: " + m_currentFrame.ToString(), fnt, Brushes.White, m_lmargin, m_lmargin);
            int frstep = 0;

            if (m_timeline == null)
                return;


            int step;

			step = (int)((Width - m_lmargin - m_rmargin) / m_timeline.TotalSeconds);
			frstep = (Width - m_lmargin - m_rmargin) / m_timeline.TotalFrames;

            // This section attempts to guess a reasonable distance between marks
            // starts with 1 as the frame stepping value (show ticks on every frame)
            // if distance's too close then double it... until we get that a second 
            // is more than 16 pixels.
            int t = 0, tstep = 1;
            while (step < 16)
            {
                tstep *= 2;
                step = (int)((Width - m_lmargin - m_rmargin) / (m_timeline.TotalSeconds/tstep));
            }

			// Draw selection background
			int xSel = ScreenCoordFromFrame(m_iSelectionBegin);
			int wSel = ScreenCoordFromFrame(m_iSelectionEnd + 1) - xSel;
			e.Graphics.FillRectangle(Brushes.Gray, new Rectangle(xSel, 30, wSel , 15));

            // Draw frame labels (every tstep frames) and tick marks
            for (int x = m_lmargin + 1; x <= (Width - m_rmargin); x += step)
            {
                e.Graphics.DrawLine(Pens.LightGray, x, 14, x, Height - 20);
                e.Graphics.DrawString(t.ToString(), fnt, Brushes.White, x, 14);
                t += tstep;
            }
            // smaller,frame level tick marks
            //for (int x = m_lmargin + 1; x <= (Width - m_rmargin); x += frstep)
            //{
            //    e.Graphics.DrawLine(Pens.LightGray, x, 14, x, Height - 42);
            //}

            // Draw green indicators for keyframes
            foreach( System.Collections.Generic.KeyValuePair<int,Keyframe> fr in m_timeline.KeyFrames)
            {
                int xpoint = ScreenCoordFromFrame(fr.Key);
                //e.Graphics.FillEllipse(Brushes.LightGreen, new Rectangle(xpoint+(int)(frstep/8), 30, 7, 7));
                e.Graphics.FillRectangle(Brushes.LightGreen, new Rectangle(xpoint + (int)(frstep / 8), 30, frstep-1, 15));
            }

            // Current frame indicator, red vertical line
            //int xcur = ScreenCoordFromTime(((float)m_currentFrame / m_timeline.FrameRate));
            int xcur = ScreenCoordFromFrame(m_currentFrame);
            e.Graphics.DrawRectangle(Pens.Red, xcur, 15, frstep, Height - 20);
        }

        public void SetFrame(int i)
        {
            if (i < 0 || i > m_timeline.TotalFrames)
                throw new Exception("Bad frame number specified");
            m_currentFrame = i;

            // report the change to others
            //OnFrameChanged(this,i);
        }

        private string TimeStringFromMouseCoord(int x)
        {
            double secs = TimeFromMouseCoord(x);
            int frame = (int)((secs - Math.Truncate(secs)) * m_timeline.FrameRate);
            secs = Math.Truncate(secs);
            return secs.ToString("000") + "s" + frame.ToString("00") + "f";
        }


        private int ScreenCoordFromTime(float t)
        {
            int barwidth = Width - m_lmargin - m_rmargin;
            int ret = 1+ m_lmargin + (int)(t * barwidth / m_timeline.TotalSeconds);
            return ret;
        }

        private double TimeFromMouseCoord(int x)
        {
            int barwidth = Width - m_lmargin - m_rmargin;
            return x * ((double)m_timeline.TotalSeconds / barwidth);
        }

        private int ScreenCoordFromFrame(int frame)
        {
            int barwidth = Width - m_lmargin - m_rmargin;
            int ret = 1 + m_lmargin + (int)((frame * barwidth) / m_timeline.TotalFrames);
            return ret;
        }
    }


}
